package com.lintf.flowable.token;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lintf.common.exception.ServiceException;
import com.lintf.flowable.domain.SysUser;
import com.lintf.flowable.token.user.UserToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author lintf
 * @desc 使用token验证用户是否登录
 **/
@Slf4j
public class TokenUtils {

    // 设置过期时间为7天
    private static final long EXPIRE_TIME_MS = TimeUnit.DAYS.toMillis(30);
    // Token秘钥
    private static final String TOKEN_SECRET = "ZCfasfhuaUUHufguGuwu2020BQWf";


    public static UserToken setUserToken(Map<String, String> params) {
        // 假设这是你的 parsedToken
        // 创建 UserToken 对象
        UserToken userToken = new UserToken();

        // 获取 UserToken 类的所有方法
        Method[] methods = UserToken.class.getMethods();

        // 遍历 parsedToken 中的键值对
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            if (StringUtils.equals("exp", key)) {
                continue;
            }
            String value = entry.getValue();
            // 构造 setter 方法名
            String setterMethodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
            Method method = Arrays.stream(methods)
                    .filter(item -> item.getName().equals(setterMethodName))
                    .findFirst()
                    .orElseThrow(() -> new NoSuchMethodError("No such setter method: " + setterMethodName));

            // 获取 setter 方法的参数类型
            Class<?> parameterType = method.getParameterTypes()[0];

            try {
                // 根据参数类型转换 value
                Object convertedValue = null;
                if (!StringUtils.equals("null", value)) {
                    convertedValue = convertValue(value, parameterType);
                }

                // 调用 setter 方法设置值
                method.invoke(userToken, convertedValue);
            } catch (Exception e) {
                log.error("解析赋值失败:" + e.getMessage(), e);
                throw new ServiceException("解析赋值失败");
            }
        }
        return userToken;
    }

    private static Object convertValue(String value, Class<?> targetType) {
        if (targetType.equals(String.class)) {
            return value;
        } else if (targetType.equals(Integer.class) || targetType.equals(int.class)) {
            return Integer.parseInt(value);
        } else if (targetType.equals(Long.class) || targetType.equals(long.class)) {
            return Long.parseLong(value);
        } else if (targetType.equals(Boolean.class) || targetType.equals(boolean.class)) {
            return Boolean.parseBoolean(value);
        } else if (targetType.equals(Double.class) || targetType.equals(double.class)) {
            return Double.parseDouble(value);
        }
        // 其他类型的转换需要根据具体情况继续添加
        throw new IllegalArgumentException("Unsupported parameter type: " + targetType.getName());
    }

    /**
     * 生成Token方法，根据手机号和openId生成Token。
     *
     * @return 生成的Token字符串
     */
    public static <T> String generateToken(T object) {
        String token = "";
        try {
            // 设置过期时间
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME_MS);
            // 设置加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            // 设置头部信息
            Map<String, Object> header = new HashMap<>();
            header.put("typ", "JWT");
            header.put("alg", "HS256");
            // 生成Token
            if (object instanceof SysUser) {
                SysUser userInfo = (SysUser) object;
                token = JWT.create()
                        .withHeader(header)
                        .withClaim("userId", String.valueOf(userInfo.getId()))
                        .withClaim("username", userInfo.getUsername())
                        .withExpiresAt(expireDate)
                        .sign(algorithm);
            }
        } catch (Exception e) {
            throw new ServiceException("Token生成失败：" + e.getMessage());
        }
        return token;
    }

    /**
     * 验证Token方法，用于检查Token的有效性。
     *
     * @param token 要验证的Token字符串
     * @return 如果Token有效返回true，否则返回false
     */
    public static boolean verifyToken(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            // 验证Token
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            // Token验证失败
            throw new ServiceException("Token校验失败：" + e.getMessage());
        }
    }

    /**
     * 解析Token方法，用于获取Token中的用户信息。
     *
     * @param token 要解析的Token字符串
     * @return 包含用户信息的Map对象，如果解析失败返回null
     */
    public static Map<String, String> parseToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Map<String, Claim> claims = jwt.getClaims();
            Map<String, String> userInfo = new HashMap<>();
            for (Map.Entry<String, Claim> entry : claims.entrySet()) {
                userInfo.put(entry.getKey(), claims.get(entry.getKey()).asString());
            }
            return userInfo;
        } catch (Exception e) {
            // Token解析失败
            throw new ServiceException("Token校验失败：" + e.getMessage());
        }
    }

    /**
     * 刷新Token，延长其有效期。
     *
     * @param oldToken 旧的Token字符串
     * @return 新的Token字符串
     */
    public static String refreshToken(String oldToken) {
        try {
            // 解析Token
            String[] parts = oldToken.split("\\.");
            if (parts.length != 3) {
                throw new IllegalArgumentException("Token格式错误");
            }

            Map<String, String> parsedToken = parseToken(oldToken);

            // 检查Token有效性
            if (!verifyToken(oldToken)) {
                throw new ServiceException("无效的Token");
            }

            // 重新生成Token，使用相同的用户信息，但更新过期时间
            return generateToken(parsedToken);
        } catch (Exception e) {
            throw new ServiceException("Token刷新失败：" + e.getMessage());
        }
    }

    public static void main(String[] args) {

    }
}
